using System.Collections;
using GHIElectronics.NETMF.Glide;
using GHIElectronics.NETMF.Glide.Display;
using GHIElectronics.NETMF.Glide.UI;
using Melody_Glider.MelodyContent;
using Microsoft.SPOT;

namespace Melody_Glider.GUIContent
{
    //todo: try this solution to make EditorWindow work faster:
    //move the content after invoking some method (i.eg. delete) and refill the remaining part of field
    //if some part of content now is out of bounds - remove child
    //if some part of content now can be pulled to the field - draw it
    //adjust the indexes of notes after changed/removed/added one
    
    class StaffDrawer 
    {
        private readonly EditorWindow _editorWindow;
        private readonly Window _relatedWindow;
        private readonly SystemState _conditions;

        private const int MelodyFieldY0 = 71;
        private const int CursorHeight = 88;
        private const int StaffY0 = 87;

        private readonly ArrayList _cursors = new ArrayList();
        private readonly Hashtable _durations = new Hashtable
        {
            {Duration.Full, Resources.GetBitmap(Resources.BitmapResources.fullNoteTh)},
            {Duration.Half, Resources.GetBitmap(Resources.BitmapResources.halfNoteTh)},
            {Duration.Quarter, Resources.GetBitmap(Resources.BitmapResources.quarterNoteTh)},
            {Duration.Eighth, Resources.GetBitmap(Resources.BitmapResources.eighthNoteTh)},
            {Duration.Sixteenth, Resources.GetBitmap(Resources.BitmapResources.sixteenthNoteTh)},
            {Duration.Full + "Rest", Resources.GetBitmap(Resources.BitmapResources.fullRestTh)},
            {Duration.Half + "Rest", Resources.GetBitmap(Resources.BitmapResources.halfRestTh)},
            {Duration.Quarter + "Rest", Resources.GetBitmap(Resources.BitmapResources.quarterRestTh)},
            {Duration.Eighth + "Rest", Resources.GetBitmap(Resources.BitmapResources.eighthRestTh)},
            {Duration.Sixteenth + "Rest", Resources.GetBitmap(Resources.BitmapResources.sixteenthRestTh)}
        };

        private readonly Hashtable _pitchLinesY = new Hashtable
                                                  {
                                                      {Pitch.C, StaffY0+1+8*6+4-13},
                                                      {Pitch.Cs, StaffY0+1+8*6+4-13},
                                                      {Pitch.D, StaffY0+1+8*6-13},
                                                      {Pitch.Eb, StaffY0+1+8*5+4-13},
                                                      {Pitch.E, StaffY0+1+8*5+4-13},
                                                      {Pitch.F, StaffY0+1+8*5-13},
                                                      {Pitch.Fs, StaffY0+1+8*5-13},
                                                      {Pitch.G, StaffY0+1+8*4+4-13},
                                                      {Pitch.Gs, StaffY0+1+8*4+4-13},
                                                      {Pitch.A, StaffY0+1+8*4-13},
                                                      {Pitch.Bb, StaffY0+1+8*3+4-13},
                                                      {Pitch.B, StaffY0+1+8*3+4-13},
                                                      {Pitch.R,StaffY0+1+8*3+4-13}
                                                  };

        private readonly Hashtable _octaves = new Hashtable
                                              {
                                                  {-1, 8*4-4},
                                                  {0, 0},
                                                  {1, -(8*3+4)}
                                              };


        public StaffDrawer(EditorWindow editorWindow, Window editorcontent, SystemState conditions)
        {
            _editorWindow = editorWindow;
            _conditions = conditions;
            _relatedWindow = editorcontent;
        }

        public void ClearMelodyField()
        {
            RemoveField();
            RemoveCursors();
            RemoveNotes();

            _editorWindow.DisplayedNoteCounter = 0;
        }

        private void RemoveField()
        {
            _relatedWindow.RemoveChild(_relatedWindow.GetChildByName("Field"));
        }

        private void RemoveCursors()
        {
            foreach (var cursor in _cursors)
            {
                _cursors.Remove(_relatedWindow.GetChildByName((string)cursor));
                _relatedWindow.RemoveChild(_relatedWindow.GetChildByName((string)cursor));
            }
        }

        private void RemoveNotes()
        {
            for (var i = _editorWindow.DrawingStartIndex - 1; i < _editorWindow.DisplayedNoteCounter + _editorWindow.DrawingStartIndex; i++)
            {
                _relatedWindow.RemoveChild(_relatedWindow.GetChildByName("Note" + i));  
                _relatedWindow.RemoveChild(_relatedWindow.GetChildByName("Sharp" + i)); //If actual note isn't i.eg. sharp there is no exception
                _relatedWindow.RemoveChild(_relatedWindow.GetChildByName("Flat" + i));  //I don't know if it is needed and possible to improve without complicating
                _relatedWindow.RemoveChild(_relatedWindow.GetChildByName("Dot" + i));
                _relatedWindow.RemoveChild(_relatedWindow.GetChildByName("Staccato" + i));
            }
        }

        public void DrawMelodyField()
        {
            DrawField();
            DrawMelody();
            UpdateScrollbar();
        }

        private void DrawField()
        {
            var drawer = GetCanvas();
            _relatedWindow.AddChild(drawer);

            drawer.DrawRectangle(Colors.Black, 1, 40, MelodyFieldY0, 241, 117, 0, 0, Colors.White, 40, MelodyFieldY0, Colors.White, 241, 117, 224);

            for (var i = 0; i < 10; i++) //tru-OOP would be if this was encapsulated into DrawLines(drawer)
            {
                if (i >= 1 && i <= 5)
                    drawer.DrawLine(Colors.Black, 1, 41, StaffY0 + 8 * (i + 1), 279, StaffY0 + 8 * (i + 1));
                else
                    drawer.DrawLine(Colors.LightGray, 1, 41, StaffY0 + 8 * (i + 1), 279, StaffY0 + 8 * (i + 1));
            }

            //drawer.Render();
        }

        private Canvas GetCanvas()
        {
            return new Canvas { Parent = _relatedWindow, X = 0, Y = 0, Width = 320, Height = 240, Name = "Field" };
        }

        private void DrawMelody()
        {
            DrawCursor(-1);

            for (var i = _editorWindow.DrawingStartIndex; i < _conditions.MelodyBuffer.GetMelody().Count; i++)
            {
                if (!DrawNote((Note)_conditions.MelodyBuffer.GetMelody()[i], i))
                    return;

                DrawCursor(i);
            }
        }

        private void UpdateScrollbar()
        {
            //todo: actually, I would like to delete progress bar unless there will be some scroll bar
            var progress = (ProgressBar)_relatedWindow.GetChildByName("ProgressBar");

            if (_conditions.MelodyBuffer.GetMelody().Count == 0)
            {
                progress.MaxValue = 1;
                progress.Value = 0;
            }
            else
            {
                progress.MaxValue = _conditions.MelodyBuffer.GetMelody().Count;
                progress.Value = _editorWindow.DisplayedNoteCounter + _editorWindow.DrawingStartIndex;
            }
            progress.Invalidate();
        }

        private bool DrawNote(Note note, int idx)
        {
            if (_editorWindow.XPos + NoteWidth(note) + EditorWindow.CursorWidth > EditorWindow.MelodyFieldBound)
                return false;

            if (note.IsSharp())
                DrawSharp((int)_pitchLinesY[note.GetPitch().Symbol] + (int)_octaves[note.GetPitch().OctaveShift], idx);
            if (note.IsFlat())
                DrawFlat((int)_pitchLinesY[note.GetPitch().Symbol] + (int)_octaves[note.GetPitch().OctaveShift], idx);
            if (note.IsStaccato())
                DrawStaccato((int)_pitchLinesY[note.GetPitch().Symbol] + (int)_octaves[note.GetPitch().OctaveShift], idx);

            Bitmap next;
            if (note.IsRest())
                next = (Bitmap)_durations[note.GetDuration() + "Rest"];
            else
                next = (Bitmap)_durations[note.GetDuration()];
            
            var noteImg = new Image("Note" + idx, 255, _editorWindow.XPos, (int)_pitchLinesY[note.GetPitch().Symbol] + (int)_octaves[note.GetPitch().OctaveShift], next.Width, next.Height);
            noteImg.Bitmap = next;
            noteImg.TapEvent += _editorWindow.ElementImgTapEvent;

            var backColor = noteImg.Bitmap.GetPixel(0, 0);
            noteImg.Bitmap.MakeTransparent(backColor);

            _relatedWindow.AddChild(noteImg);

            _editorWindow.XPos += next.Width;
            _editorWindow.DisplayedNoteCounter++;
            
            if (note.IsDotted())
                DrawDot((int)_pitchLinesY[note.GetPitch().Symbol] + (int)_octaves[note.GetPitch().OctaveShift], idx);
            
            return true;
        }

        public int NoteWidth(Note analyzedOne)
        {
            var total = 0;

            Bitmap next;
            if (analyzedOne.IsRest())
                next = (Bitmap)_durations[analyzedOne.GetDuration() + "Rest"];
            else
                next = (Bitmap)_durations[analyzedOne.GetDuration()];
            
            total += next.Width;
            
            if (analyzedOne.IsFlat())
                total += 9;
            if (analyzedOne.IsSharp())
                total += 13;
            if (analyzedOne.IsDotted())
                total += 6;
            
            return total;
        }

        private void DrawCursor(int idx)
        {
            var cursor = new Image("Cursor" + idx, 4, _editorWindow.XPos, MelodyFieldY0 + 17, EditorWindow.CursorWidth, CursorHeight);
            cursor.Bitmap = Resources.GetBitmap(Resources.BitmapResources.cursor);
            cursor.TapEvent += _editorWindow.ElementImgTapEvent;

            _relatedWindow.AddChild(cursor);
            _cursors.Add("Cursor" + idx);
            
            _editorWindow.XPos += EditorWindow.CursorWidth;
        }

        private void DrawDot(int noteY, int idx)
        {
            var next = Resources.GetBitmap(Resources.BitmapResources.dotTh);
            var dotImg = new Image("Dot" + idx, 255, _editorWindow.XPos + 2, noteY + 16, next.Width, next.Height);
            dotImg.Bitmap = next;
            dotImg.TapEvent += _editorWindow.ElementImgTapEvent;

            var backColor = dotImg.Bitmap.GetPixel(0, 0);
            dotImg.Bitmap.MakeTransparent(backColor);
            
            _relatedWindow.AddChild(dotImg);
            
            _editorWindow.XPos += next.Width + 2;
        }

        private void DrawStaccato(int noteY, int idx)
        {
            var next = Resources.GetBitmap(Resources.BitmapResources.dotTh);
            var dotImg = new Image("Staccato" + idx, 255, _editorWindow.XPos + 4, noteY + 23, next.Width, next.Height);
            dotImg.Bitmap = next;
            dotImg.TapEvent += _editorWindow.ElementImgTapEvent;
            
            var backColor = dotImg.Bitmap.GetPixel(0, 0);
            dotImg.Bitmap.MakeTransparent(backColor);
            
            _relatedWindow.AddChild(dotImg);
        }

        private void DrawSharp(int noteY, int idx)
        {
            var next = Resources.GetBitmap(Resources.BitmapResources.sharpTh);
            var sharpImg = new Image("Sharp" + idx, 255, _editorWindow.XPos, noteY + 9, next.Width, next.Height);
            sharpImg.Bitmap = next;
            sharpImg.TapEvent += _editorWindow.ElementImgTapEvent;
            
            var backColor = sharpImg.Bitmap.GetPixel(0, 0);
            sharpImg.Bitmap.MakeTransparent(backColor);
            
            _relatedWindow.AddChild(sharpImg);
            
            _editorWindow.XPos += next.Width;
        }

        private void DrawFlat(int noteY, int idx)
        {
            var next = Resources.GetBitmap(Resources.BitmapResources.flatTh); //todo: try to combine dot/staccato/sharp/flat drawing into one method - hashtable?
            var flatImg = new Image("Flat" + idx, 255, _editorWindow.XPos, noteY + 5, next.Width, next.Height);
            flatImg.Bitmap = next;
            flatImg.TapEvent += _editorWindow.ElementImgTapEvent;
            
            var backColor = flatImg.Bitmap.GetPixel(0, 0);
            flatImg.Bitmap.MakeTransparent(backColor);
            
            _relatedWindow.AddChild(flatImg);
            
            _editorWindow.XPos += next.Width;
        }
    }
}
